package hc

import (
	"context"
	"os"
	"strings"
	"time"

	"go.uber.org/zap"
	"go.uber.org/zap/zapcore"
	"gopkg.in/natefinch/lumberjack.v2"
)

// LoggerObj  zap日志
type LoggerObj struct {
	*zap.Logger
}

// newLogger 新建logger
//  * filePath 日志文件路径
//  * level 日志级别
//  * maxSize 每个日志文件保存的最大尺寸 单位：M
//  * maxBackups 日志文件最多保存多少个备份
//  * maxAge 文件最多保存多少天
//  * compress 是否压缩
func newLogger(filePath string, level zapcore.Level) *zap.Logger {
	core := newCore(filePath, level)
	if Debug {
		return zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1), zap.Development())
	}
	return zap.New(core, zap.AddCaller(), zap.AddCallerSkip(1), zap.Fields(zap.String("serviceName", ServiceName)))
}

/**
 * zapcore构造
 */
func newCore(filePath string, level zapcore.Level) zapcore.Core {
	// 设置日志级别
	atomicLevel := zap.NewAtomicLevel()
	atomicLevel.SetLevel(level)
	//公用编码器
	encoderConfig := zapcore.EncoderConfig{
		TimeKey:       "time",
		LevelKey:      "level",
		NameKey:       "logger",
		CallerKey:     "caller",
		MessageKey:    "msg",
		StacktraceKey: "stacktrace",
		LineEnding:    zapcore.DefaultLineEnding,
		EncodeLevel:   zapcore.LowercaseLevelEncoder, // 小写编码器
		// EncodeLevel:    zapcore.CapitalColorLevelEncoder, //这里可以指定颜色
		EncodeTime:     shortTimeEncoder,               // ISO8601 UTC 时间格式
		EncodeDuration: zapcore.SecondsDurationEncoder, //
		EncodeCaller:   zapcore.ShortCallerEncoder,     // 简短路径编码器, 全路径编码器用zapcore.FullCallerEncoder,
		EncodeName:     zapcore.FullNameEncoder,
	}
	var ws zapcore.WriteSyncer
	var encoding zapcore.Encoder
	if Debug {
		encoderConfig.EncodeTime = shortTimeEncoder
		encoderConfig.EncodeLevel = zapcore.CapitalColorLevelEncoder //这里可以指定颜色
		ws = zapcore.AddSync(os.Stdout)                              // 打印到控制台
		// encoderConfig.ConsoleSeparator = "|"
		encoding = zapcore.NewConsoleEncoder(encoderConfig)
	} else {
		encoderConfig.EncodeTime = longTimeEncoder
		//日志文件路径配置
		hook := lumberjack.Logger{
			Filename:   filePath,      // 日志文件路径
			MaxSize:    logMaxSize,    // 每个日志文件保存的最大尺寸 单位：M
			MaxBackups: logMaxBackups, // 日志文件最多保存多少个备份
			MaxAge:     logMaxAge,     // 文件最多保存多少天
			Compress:   logIsCompress, // 是否压缩
		}
		ws = zapcore.AddSync(&hook) // 打印到文件
		encoding = zapcore.NewJSONEncoder(encoderConfig)

	}
	return zapcore.NewCore(
		encoding,                        // 编码器配置
		zapcore.NewMultiWriteSyncer(ws), // 打印到控制台和文件
		atomicLevel,                     // 日志级别
	)
}

func shortTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
	enc.AppendString(t.Format(time.Kitchen))
}

func longTimeEncoder(t time.Time, enc zapcore.PrimitiveArrayEncoder) {
	enc.AppendString(t.Format("2006-01-02 15:04:05"))
}

// 日志变量
var (
	InfoLogger  LoggerObj // info级别日志
	DebugLogger LoggerObj // debug级别日志
	WarnLogger  LoggerObj // warn级别日志
	ErrorLogger LoggerObj // error级别日志

	logMaxSize    = 128   // 每个日志文件保存的最大尺寸 单位：M
	logMaxBackups = 7     // 日志文件最多保存多少个备份
	logMaxAge     = 2     // 文件最多保存多少天
	logIsCompress = false // 是否压缩

)

// InitLog 使用日志配置变量初始化日志
func InitLog() {
	if !strings.HasSuffix(LogDir, "/") {
		LogDir += "/"
	}
	DebugLogger.Logger = newLogger(LogDir+ServiceName+".debug.log", zapcore.DebugLevel)
	InfoLogger.Logger = newLogger(LogDir+ServiceName+".info.log", zapcore.InfoLevel)
	WarnLogger.Logger = newLogger(LogDir+ServiceName+".warn.log", zapcore.WarnLevel)
	ErrorLogger.Logger = newLogger(LogDir+ServiceName+".error.log", zapcore.ErrorLevel)
}

// LoggerWithBase LoggerWithBase
type LoggerWithBase struct {
	base LogBase
}

// LogBase 日志基础信息
type LogBase struct {
	ClientURI    string `json:"clientURI"`    // 客户端请求地址
	URI          string `json:"uri"`          // 当前服务被请求地址(service/receiver/method)
	StartTime    int64  `json:"startTime"`    // 服务器接收到客户端请求的时间
	RequestTime  int64  `json:"requestTime"`  // 请求当前当前服务的发起时间
	Platform     string `json:"platform"`     // 客户端的平台
	SpanLog      bool   `json:"spanLog"`      // 是否是span日志
	IP           string `json:"ip"`           // 客户端ip
	TraceID      string `json:"traceID"`      // traceID
	AppName      string `json:"appName"`      // appName
	AppCode      string `json:"appCode"`      // appCode
	AppVersion   string `json:"appVersion"`   // appVersion
	ReceiveTime  int64  `json:"receiveTime"`  // 当前服务接收到请求的时间
	ResponseTime int64  `json:"responseTime"` // 当前服务响应请求的时间
}

// Log 使用Context相关参数
func Log(ctx context.Context) *LoggerWithBase {
	l := new(LoggerWithBase)
	if clientURI := ctx.Value(ContextKey("ClientURI")); clientURI != nil {
		l.base.ClientURI = clientURI.(string)
	}
	if uri := ctx.Value(ContextKey("URI")); uri != nil {
		l.base.URI = uri.(string)
	}
	if appCode := ctx.Value(ContextKey("AppCode")); appCode != nil {
		l.base.AppCode = appCode.(string)
	}
	if appCode := ctx.Value(ContextKey("AppCode")); appCode != nil {
		l.base.AppCode = appCode.(string)
	}
	if appName := ctx.Value(ContextKey("AppName")); appName != nil {
		l.base.AppName = appName.(string)
	}
	if appVersion := ctx.Value(ContextKey("AppVersion")); appVersion != nil {
		l.base.AppVersion = appVersion.(string)
	}

	if ip := ctx.Value(ContextKey("IP")); ip != nil {
		l.base.IP = ip.(string)
	}
	if platform := ctx.Value(ContextKey("Platform")); platform != nil {
		l.base.Platform = platform.(string)
	}
	if traceID := ctx.Value(ContextKey("TraceID")); traceID != nil {
		l.base.TraceID = traceID.(string)
	}
	if startTime := ctx.Value(ContextKey("StartTime")); startTime != nil {
		l.base.StartTime = startTime.(int64)
	}
	if requestTime := ctx.Value(ContextKey("RequestTime")); requestTime != nil {
		l.base.RequestTime = requestTime.(int64)
	}
	if receiveTime := ctx.Value(ContextKey("ReceiveTime")); receiveTime != nil {
		l.base.ReceiveTime = receiveTime.(int64)
	}
	if responseTime := ctx.Value(ContextKey("ResponseTime")); responseTime != nil {
		l.base.ResponseTime = responseTime.(int64)
	}
	if spanLog := ctx.Value(ContextKey("SpanLog")); spanLog != nil {
		l.base.SpanLog = spanLog.(bool)
	}
	return l
}

// Debug debug日志实现
func (l *LoggerWithBase) Debug(msg string, fields ...interface{}) {
	if len(fields) > 0 {
		result := fields[0]
		if Debug {
			DebugLogger.Debug(msg, zap.Any("content", result))
		} else {
			DebugLogger.Debug(msg, zap.Any("base", l.base), zap.Any("content", result))
		}
		return
	}
	if Debug {
		DebugLogger.Debug(msg)
	} else {
		DebugLogger.Debug(msg, zap.Any("base", l.base))
	}
}

// Info info日志实现
func (l *LoggerWithBase) Info(msg string, fields ...interface{}) {
	if len(fields) > 0 {
		result := fields[0]
		if Debug {
			InfoLogger.Info(msg, zap.Any("content", result))
		} else {
			InfoLogger.Info(msg, zap.Any("base", l.base), zap.Any("content", result))
		}
		return
	}
	if Debug {
		InfoLogger.Info(msg)
	} else {
		InfoLogger.Info(msg, zap.Any("base", l.base))
	}
}

// Warn warn日志实现
func (l *LoggerWithBase) Warn(msg string, fields ...interface{}) {
	if len(fields) > 0 {
		result := fields[0]
		if Debug {
			WarnLogger.Warn(msg, zap.Any("content", result))
		} else {
			WarnLogger.Warn(msg, zap.Any("base", l.base), zap.Any("content", result))
		}
		return
	}
	if Debug {
		WarnLogger.Warn(msg)
	} else {
		WarnLogger.Warn(msg, zap.Any("base", l.base))
	}
}

// Error error日志实现
func (l *LoggerWithBase) Error(msg string, fields ...interface{}) {
	if len(fields) > 0 {
		result := fields[0]
		if Debug {
			ErrorLogger.Error(msg, zap.Any("content", result))
		} else {
			ErrorLogger.Error(msg, zap.Any("base", l.base), zap.Any("content", result))
		}
		return
	}
	if Debug {
		ErrorLogger.Error(msg)
	} else {
		ErrorLogger.Error(msg, zap.Any("base", l.base))
	}
}
